﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace Sublimation
{
    public class Tower : GameObject
    {
        //these factor are for general upgrade
        protected float rangeFactor;
        public float RangedFactor
        {
            get { return this.rangeFactor; }
            set { rangeFactor = value; }
        }
        protected float damageFactor;
        public float DamageFactor
        {
            get { return this.damageFactor; }
            set { damageFactor = value; }
        }
        protected float unFrozeSpeed;
        protected float iceEffect;
        protected float rateFactor;
        public float RateFactor
        {
            get { return this.rateFactor * iceEffect; }
            set { rateFactor = value; }
        }

        protected bool buiding;
        public bool Building
        {
            get { return buiding; }
        }
        protected bool mouving;
        public bool Mouving
        {
            get{return mouving;}
            set { mouving = value; }
        }
        protected TowerEnergy energy;
        public TowerEnergy Energy
        {
            get { return this.energy; }
        }
        protected Shield shield; //starts with 0 energy so permanently collapsed and components can add or equivalent
        public Shield Shield
        {
            get { return this.shield; }
        }
        protected Player owner;
        protected Texture2D tower_top;
        protected Texture2D tower_bottom;

        protected Slot[] slots = new Slot[6]; //XXX:lets make these separate variables for clarity - then again we can loop over these
        public Slot[] Slots
        {
            get { return slots; }
            set { slots = value; }
        }



        //the maximum diameter of a tower
        public const float RADIUS = 50;

        protected int production;

        public Tower(WorldProperties worldProperties, Player owner, Vector2 position,bool built)
            : base(worldProperties)
        {
            rangeFactor = 1f;
            damageFactor = 1f;
            rateFactor = 1f;
            iceEffect = 1f;
            unFrozeSpeed = 0.01f;

            LoadContent();
            Scale = 1.0f;

            this.worldProperties = worldProperties;
            this.buiding = !built;
            SetOriginToCentre();
            Position = position;
            Rotation = 0;
            production = 100;
            shield = new Shield(worldProperties, this, 0, 0, 0, 3000);

            this.energy = new TowerEnergy(worldProperties, this);
            this.owner = owner;

            upgradeSlot(0, SlotKind.Base_weapon);
            upgradeSlot(1, SlotKind.Energy_transmition);
            upgradeSlot(2, SlotKind.Empty);
            upgradeSlot(3, SlotKind.Empty);
            upgradeSlot(4, SlotKind.Empty);
            upgradeSlot(5, SlotKind.Empty);


            // make sure it loads and draws
            DrawOrder = 50;
            UpdateOrder = 50;
            Visible = true;
            Enabled = true;
        }

        public void upgradeSlot(int slotId, SlotKind kind)
        {
            XmlSerializer s = new XmlSerializer(typeof(SlotProperties));
            SlotProperties slotP;
            TextReader r = null;
            slotP = new SlotProperties();
            if (slots[slotId] != null)
            {
                slots[slotId].Kill();
            }
            switch (kind)
            {
                case SlotKind.Empty:
                    slots[slotId] = new Slot(worldProperties, kind, this,true);
                    break;
                case SlotKind.Attractor:
                    r = new StreamReader("Resources/XML/slots/attractor.xml");
                    slots[slotId] = new SlotManipulator(worldProperties, kind, this);
                    break;
                case SlotKind.AttractorFree:
                    r = new StreamReader("Resources/XML/slots/attractor_free.xml");
                    slots[slotId] = new SlotManipulator(worldProperties, kind, this);
                    break;
                case SlotKind.Energy_transmition:
                    r = new StreamReader("Resources/XML/slots/energy.xml");
                    slots[slotId] = new SlotEnergy(worldProperties, kind, this);
                    break;
                case SlotKind.Energy_transmition2:
                    r = new StreamReader("Resources/XML/slots/energy2.xml");
                    slots[slotId] = new SlotEnergy(worldProperties, kind, this);
                    break;
                case SlotKind.Flame:
                    r = new StreamReader("Resources/XML/slots/flame.xml");
                    slots[slotId] = new SlotFlame(worldProperties, kind, this);
                    break;
                case SlotKind.General_up:
                    r = new StreamReader("Resources/XML/slots/general_up.xml");
                    slots[slotId] = new SlotTechnology(worldProperties, kind, this);
                    break;
                case SlotKind.Lightning:
                    r = new StreamReader("Resources/XML/slots/lightning.xml");
                    slots[slotId] = new SlotLightning(worldProperties, kind, this);
                    break;
                case SlotKind.Mine:
                    r = new StreamReader("Resources/XML/slots/mine.xml");
                    slots[slotId] = new SlotMines(worldProperties, kind, this);
                    break;
                case SlotKind.NeedleGun:
                    r = new StreamReader("Resources/XML/slots/needle_gun.xml");
                    slots[slotId] = new SlotWeapon(worldProperties, kind, this, false);
                    break;
                case SlotKind.Repulsor:
                    r = new StreamReader("Resources/XML/slots/repulsor.xml");
                    slots[slotId] = new SlotManipulator(worldProperties, kind, this);
                    break;
                case SlotKind.RepulsorFree:
                    r = new StreamReader("Resources/XML/slots/repulsor_free.xml");
                    slots[slotId] = new SlotManipulator(worldProperties, kind, this);
                    break;
                case SlotKind.Sheild:
                    r = new StreamReader("Resources/XML/slots/shield.xml");
                    slots[slotId] = new SlotTechnology(worldProperties, kind, this);
                    break;
                case SlotKind.Sniper:
                    r = new StreamReader("Resources/XML/slots/sniper.xml");
                    slots[slotId] = new SlotSniper(worldProperties, kind, this);
                    break;
                case SlotKind.Base_weapon:
                    r = new StreamReader("Resources/XML/slots/weapon.xml");
                    slots[slotId] = new SlotWeapon(worldProperties, kind, this, true);
                    break;
                case SlotKind.Weapon_up:
                    r = new StreamReader("Resources/XML/slots/weapon_up.xml");
                    slots[slotId] = new SlotTechnology(worldProperties, kind, this);
                    break;
                case SlotKind.Base_weapon2:
                    r = new StreamReader("Resources/XML/slots/weapon2.xml");
                    slots[slotId] = new SlotWeapon(worldProperties, kind, this, false);
                    break;
                default:
                    r = null;
                    break;
            }

            if (r != null)
            {
                slotP = (SlotProperties)s.Deserialize(r);
                r.Close();
                slots[slotId].Damage = slotP.Damage;
                slots[slotId].AttackDelay = slotP.AttackDelay;
                slots[slotId].Production_cost = slotP.Production_cost;
                slots[slotId].Angle = slotP.Angle;
                slots[slotId].Range = slotP.Range;
            }

            switch (slotId)
            {
                case 0:
                    slots[slotId].Orientation = 0;
                    break;
                case 1:
                    slots[slotId].Orientation = MathHelper.Pi;
                    break;
                case 2:
                    slots[slotId].Orientation = 5 * MathHelper.PiOver4;
                    break;
                case 3:
                    slots[slotId].Orientation = 3 * MathHelper.PiOver4;
                    break;
                case 4:
                    slots[slotId].Orientation = 7 * MathHelper.PiOver4;
                    break;
                case 5:
                    slots[slotId].Orientation =  MathHelper.PiOver4;
                    break;
            }
        }

        public virtual Slot createSlotEnergy()
        {
            return  new SlotEnergy(worldProperties, SlotKind.Energy_transmition, this);
        }

        public override void Update(GameTime gameTime)
        {
            if (!buiding)
            {
                base.Update(gameTime);
            }
            else
            {
                if (energy.Amount > TowerEnergy.DEFAULT_BUILDING_AMOUNT)
                {
                    buiding = false;
                    foreach (Slot slot in slots)
                    {
                        slot.Enabled = true;
                    }
                }
            }
            if (iceEffect < 1)
            {
                iceEffect = Math.Min(iceEffect+unFrozeSpeed, 1);
            }
        }

        protected override void LoadContent()
        {
            base.LoadContent();

            tower_bottom = Game.Content.Load<Texture2D>("Sprites/t_basement");
            Sprite = Game.Content.Load<Texture2D>("Sprites/t_rotate");

            CollisionRadius = RADIUS;
        }

        public override void Draw(GameTime gameTime)
        {
            
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            if (buiding)
            {
                spriteBatch.Draw(tower_bottom, position, null, Color.Green, 0, origin, 1, SpriteEffects.None, 1);

            }
            else
            {
                spriteBatch.Draw(tower_bottom, position, null, Color.White, 0, origin, 1, SpriteEffects.None, 1);
            } 
            spriteBatch.End();
            base.Draw(gameTime);
        }

        public override void ApplyDamage(float damage)
        {
            base.ApplyDamage(damage);

            // remove damage from shield if there is any
            if (Shield != null && Shield.ShieldEnergy > 0)
            {
                Shield.ApplyDamage(damage);
            }
            else
            {
                Energy.ApplyDamage(damage);
            }
        }

        public void ApplyIce(float damage)
        {
            ApplyDamage(damage);

            if (iceEffect > 0.2)
            {
                iceEffect -= damage / 100;
            }

        }

        public override void Kill()
        {
            base.Kill();

            // cleanup components
            if (Shield != null) Shield.Kill();
            if (Energy != null) Energy.Kill();

            //TODO: kill slots
            foreach(Slot slot in slots) {
                if (slot != null) slot.Kill();
            }

            // spawn an explosion
            new Explosion(worldProperties, this, 1.5f);

            //TODO: victory conditions
            int towerCount = 0;
            foreach (Player player in worldProperties.Players)
            {
                foreach (Tower tower in player.Towers)
                {
                    if (tower != this) towerCount++;
                }
            }
            if (towerCount == 0) worldProperties.EndGame();

            // remove from player's tower list
            worldProperties.Players[0].Towers.Remove(this);
            //worldProperties.Trash.RemovableTowers.Add(this);
        }
    }
}
